#include "cjthread.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <pthread.h>
#include <ctype.h>

// 共享数据结构
typedef struct {
    char* keyword;          // 搜索关键词
    int total_files;        // 总搜索文件数
    int matched_files;      // 匹配的文件数
    pthread_mutex_t mutex;  // 互斥锁
    CThread** threads;      // 线程数组
    int thread_count;       // 线程数量
    volatile int stop_flag; // 全局停止标志
    volatile int active_threads; // 活跃线程数（含递归子线程）
    pthread_cond_t all_done_cond; // 所有线程完成的条件变量
    pthread_mutex_t done_mutex;   // 条件变量的互斥锁
    int debug;              // 调试模式（默认启用）
} SearchContext;

// 线程数据
typedef struct {
    SearchContext* ctx;     // 共享上下文
    char* dir;              // 目标目录
} ThreadData;

// 路径兼容处理
static void build_path(char* dest, const char* dir, const char* name, size_t dest_size) {
    #ifdef _WIN32
    if (dir[strlen(dir)-1] == '\\' || dir[strlen(dir)-1] == '/') {
        snprintf(dest, dest_size, "%s%s", dir, name);
    } else {
        snprintf(dest, dest_size, "%s\\%s", dir, name);
    }
    #else
    if (dir[strlen(dir)-1] == '/') {
        snprintf(dest, dest_size, "%s%s", dir, name);
    } else {
        snprintf(dest, dest_size, "%s/%s", dir, name);
    }
    #endif
}

// 文本文件判断
static int is_text_file(const char* filename, SearchContext* ctx) {
    // 基于文件扩展名判断文本文件
    const char* ext = strrchr(filename, '.');
    if (ext) {
        if (strcmp(ext, ".c") == 0 || strcmp(ext, ".h") == 0 ||
            strcmp(ext, ".cpp") == 0 || strcmp(ext, ".hpp") == 0 ||
            strcmp(ext, ".txt") == 0 || strcmp(ext, ".log") == 0 ||
            strcmp(ext, ".bat") == 0 || strcmp(ext, ".sh") == 0 ||
            strcmp(ext, ".md") == 0) {
            return 1; // 已知文本文件类型
        }
    }

    // 对未知类型文件进行内容检查
    FILE* f = fopen(filename, "rb");
    if (!f) return 0;

    int binary_chars = 0;
    unsigned char buf[512];
    size_t n = fread(buf, 1, sizeof(buf), f);
    fclose(f);
    
    // 统计不可打印字符数量
    for (size_t i = 0; i < n; i++) {
        if (buf[i] == 0) {
            // 包含空字节，视为二进制
            if (ctx->debug) {
                pthread_mutex_lock(&ctx->mutex);
                printf("调试: %s 包含空字节，视为二进制文件\n", filename);
                pthread_mutex_unlock(&ctx->mutex);
            }
            return 0;
        }
        if (!isprint(buf[i]) && !isspace(buf[i])) {
            binary_chars++;
        }
    }
    
    // 允许一定比例的不可打印字符
    int result = (binary_chars < n / 5); // 不可打印字符少于20%
    
    if (ctx->debug && !result) {
        pthread_mutex_lock(&ctx->mutex);
        printf("调试: %s 不可打印字符过多，视为二进制文件\n", filename);
        pthread_mutex_unlock(&ctx->mutex);
    }
    
    return result;
}

// 文件内容匹配函数
static int file_contains_keyword(const char* filename, const char* keyword, SearchContext* ctx) {
    // 只搜索文本文件
    if (!is_text_file(filename, ctx)) {
        return 0;
    }

    FILE* f = fopen(filename, "r");
    if (!f) {
        if (ctx->debug) {
            pthread_mutex_lock(&ctx->mutex);
            printf("调试: 无法打开文件 %s\n", filename);
            pthread_mutex_unlock(&ctx->mutex);
        }
        return 0;
    }

    // 调试模式下显示正在检查的文件
    if (ctx->debug) {
        pthread_mutex_lock(&ctx->mutex);
        printf("调试: 检查文件 %s\n", filename);
        pthread_mutex_unlock(&ctx->mutex);
    }

    char buffer[4096];
    while (fgets(buffer, sizeof(buffer), f)) {
        // 搜索关键词
        if (strstr(buffer, keyword) != NULL) {
            fclose(f);
            return 1;
        }
    }

    fclose(f);
    return 0;
}

// 递归搜索目录（线程函数）
static void search_dir_thread(void* user_data, bool (*is_stopped)(void*)) {
    ThreadData* data = (ThreadData*)user_data;
    SearchContext* ctx = data->ctx;
    
    // 增加活跃线程计数（严格加锁保护）
    pthread_mutex_lock(&ctx->done_mutex);
    ctx->active_threads++;
    pthread_mutex_unlock(&ctx->done_mutex);

    // 调试输出
    if (ctx->debug) {
        pthread_mutex_lock(&ctx->mutex);
        printf("调试: 线程开始搜索: %s (当前活跃线程: %d)\n", data->dir, ctx->active_threads);
        pthread_mutex_unlock(&ctx->mutex);
    }

    DIR* dir = opendir(data->dir);
    if (!dir) {
        if (ctx->debug) {
            pthread_mutex_lock(&ctx->mutex);
            printf("调试: 无法打开目录 %s\n", data->dir);
            pthread_mutex_unlock(&ctx->mutex);
        }
        
        // 减少活跃线程计数
        pthread_mutex_lock(&ctx->done_mutex);
        ctx->active_threads--;
        if (ctx->active_threads == 0) {
            pthread_cond_signal(&ctx->all_done_cond); // 通知主线程
        }
        pthread_mutex_unlock(&ctx->done_mutex);
        free(data);
        return;
    }

    struct dirent* entry;
    while ((entry = readdir(dir)) != NULL) {
        // 检查停止信号
        if (is_stopped(ctx) || ctx->stop_flag) {
            break;
        }

        // 跳过.和..目录
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        // 构建完整路径
        char path[1024];
        build_path(path, data->dir, entry->d_name, sizeof(path));

        // 判断文件类型
        struct stat st;
        if (stat(path, &st) != 0) {
            continue;
        }

        if (S_ISDIR(st.st_mode)) {
            // 递归搜索子目录（创建新线程数据）
            ThreadData* sub_data = (ThreadData*)malloc(sizeof(ThreadData));
            sub_data->ctx = ctx;
            sub_data->dir = strdup(path);
            search_dir_thread(sub_data, is_stopped); // 递归调用
        } else if (S_ISREG(st.st_mode)) {
            // 统计总文件数
            pthread_mutex_lock(&ctx->mutex);
            ctx->total_files++;
            pthread_mutex_unlock(&ctx->mutex);

            // 检查文件内容
            if (file_contains_keyword(path, ctx->keyword, ctx)) {
                pthread_mutex_lock(&ctx->mutex);
                ctx->matched_files++;
                printf("[匹配] %s\n", path);
                pthread_mutex_unlock(&ctx->mutex);
            }
        }
    }

    closedir(dir);
    free(data->dir);
    free(data);

    // 减少活跃线程计数（严格加锁保护）
    pthread_mutex_lock(&ctx->done_mutex);
    ctx->active_threads--;
    if (ctx->active_threads == 0) {
        pthread_cond_signal(&ctx->all_done_cond); // 通知主线程所有线程完成
    }
    pthread_mutex_unlock(&ctx->done_mutex);
}

// 信号处理函数
static void handle_sigint(int sig) {
    (void)sig;
    printf("\n收到停止信号，正在终止所有线程...\n");
}

// 初始化搜索上下文
static SearchContext* init_search_context(const char* keyword, int thread_count) {
    SearchContext* ctx = (SearchContext*)malloc(sizeof(SearchContext));
    if (!ctx) return NULL;

    ctx->keyword = strdup(keyword);
    ctx->total_files = 0;
    ctx->matched_files = 0;
    ctx->stop_flag = 0;
    ctx->thread_count = thread_count;
    ctx->active_threads = 0;
    ctx->debug = 1; // 默认启用调试模式
    ctx->threads = (CThread**)malloc(sizeof(CThread*) * thread_count);
    
    // 初始化同步原语
    pthread_mutex_init(&ctx->mutex, NULL);
    pthread_mutex_init(&ctx->done_mutex, NULL);
    pthread_cond_init(&ctx->all_done_cond, NULL);

    return ctx;
}

// 释放搜索上下文
static void free_search_context(SearchContext* ctx) {
    if (!ctx) return;
    free(ctx->keyword);
    pthread_mutex_destroy(&ctx->mutex);
    pthread_mutex_destroy(&ctx->done_mutex);
    pthread_cond_destroy(&ctx->all_done_cond);
    free(ctx->threads);
    free(ctx);
}

// 显示帮助信息
static void print_help(const char* progname) {
    printf("多线程文件搜索工具（类grep功能）\n");
    printf("用法: %s <关键词> <目录1> [目录2...]\n", progname);
    printf("功能: 递归搜索指定目录中包含关键词的文本文件（默认显示调试信息）\n");
    printf("示例:\n");
    printf("  %s \"main\" .           # 搜索当前目录下含\"main\"的文件\n", progname);
    printf("  %s \"pthread\" src/ include/  # 搜索多个目录\n", progname);
}

int main(int argc, char* argv[]) {
    // 检查参数（无需解析-d选项，默认启用调试）
    if (argc < 3) {
        print_help(argv[0]);
        return 1;
    }

    // 解析参数
    const char* keyword = argv[1];
    int dir_count = argc - 2;
    char** dirs = &argv[2];

    // 初始化上下文（默认启用调试）
    SearchContext* ctx = init_search_context(keyword, dir_count);
    if (!ctx) {
        fprintf(stderr, "初始化失败: 内存分配错误\n");
        return 1;
    }

    // 注册信号处理
    signal(SIGINT, handle_sigint);

    // 记录开始时间
    clock_t start_time = clock();

    // 创建线程
    printf("开始搜索关键词: \"%s\"，目录数: %d，线程数: %d\n",
           keyword, dir_count, dir_count);
    printf("调试模式已默认启用\n");

    int create_success = 0;
    for (int i = 0; i < dir_count; i++) {
        ThreadData* data = (ThreadData*)malloc(sizeof(ThreadData));
        data->ctx = ctx;
        data->dir = strdup(dirs[i]);

        ctx->threads[i] = cjthread_create(search_dir_thread, data);
        if (ctx->threads[i]) {
            create_success++;
            printf("线程 %d 创建成功（搜索目录: %s）\n", i, dirs[i]);
        } else {
            fprintf(stderr, "线程 %d 创建失败（目录: %s）\n", i, dirs[i]);
            free(data->dir);
            free(data);
        }
    }

    if (create_success == 0) {
        fprintf(stderr, "所有线程创建失败，程序退出\n");
        free_search_context(ctx);
        return 1;
    }

    // 等待所有线程完成（核心改进：严格等待活跃线程数为0）
    printf("主线程等待所有搜索线程完成...\n");
    pthread_mutex_lock(&ctx->done_mutex);
    // 循环等待，直到活跃线程数为0（防止虚假唤醒）
    while (ctx->active_threads > 0) {
        // 设置超时等待（100ms），避免永久阻塞
        struct timespec ts;
        clock_gettime(CLOCK_REALTIME, &ts);
        ts.tv_nsec += 100000000; // 100ms
        if (ts.tv_nsec >= 1000000000) {
            ts.tv_sec++;
            ts.tv_nsec -= 1000000000;
        }
        // 等待条件变量，超时后重新检查
        pthread_cond_timedwait(&ctx->all_done_cond, &ctx->done_mutex, &ts);
        
        // 检查是否收到停止信号
        if (ctx->stop_flag) {
            break;
        }
    }
    pthread_mutex_unlock(&ctx->done_mutex);

    // 销毁所有线程
    for (int i = 0; i < dir_count; i++) {
        if (ctx->threads[i]) {
            if (cjthread_joinable(ctx->threads[i])) {
                cjthread_join(ctx->threads[i]);
            }
            cjthread_destroy(ctx->threads[i]);
        }
    }

    // 计算耗时
    clock_t end_time = clock();
    double elapsed = (double)(end_time - start_time) / CLOCKS_PER_SEC;

    // 输出统计结果
    printf("\n===== 搜索完成 =====\n");
    printf("总搜索文件: %d 个\n", ctx->total_files);
    if (ctx->matched_files == 0) {
        printf("未找到包含关键词 \"%s\" 的文件\n", keyword);
    } else {
        printf("匹配文件: %d 个\n", ctx->matched_files);
    }
    printf("耗时: %.2f 秒\n", elapsed);

    // 清理资源
    free_search_context(ctx);
    return 0;
}